【严格模式】StrictMode 引发的组件重复执行 | 您所在的位置:网站首页 › js click事件多次执行 › 【严格模式】StrictMode 引发的组件重复执行 |
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。 前言2022年3月29号,React 18正式版发布,小明也兴冲冲地开始hooks学习之旅。某天他使用codesandbox写了一个小demo时,他发现组件诡异地渲染了多次,函数组件代码如下: count.jsx export default function Count() { const [counter, setCounter] = useState(0); useEffect(() => { console.log("effect"); setTimeout(() => { setCounter(counter + 1); }, 3000); }); console.log("before render"); return ( {counter} ); } 复制代码实现的效果很简单,每隔三秒将count加1,很简单对不对? 打开这个 demo,同时打开控制台,你就可以看到如下输入: before render与effect一开始都打印了两次,之后before render每次都诡异地打印了两次 React.StrictMode这是为什么呢?经过一番查找,发现是React.StrictMode的锅。React 17文档中是这样描述 React.StrictMode: StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。 React 17文档中关于它的作用大概可以归为两类: 检测副作用 对于在应用中使用已经废弃、过时的方法会发出警告对于第二点,相信大家都可以理解,毕竟React现在都发布18版了,不少以前的方法已经过时或者废弃了,在较新版本的React中再使用这些方法肯定时不安全的。 那么对于第一点呢? 这不得不提React 18文档对于StrictMode的描述: React offers a “Strict Mode” in which it calls each component’s function twice during development. 大意就是在开发者模式中,StrictMode会将相应组件执行两次,这下重复执行的疑惑解决了。 但新的疑问产生了,文档中一直提的副作用又是啥?副作用这个词在React 17文档中提到的次数很多,如何理解它呢? 在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响 -- 维基百科 举个通俗易懂的例子:张三不小心感冒了,鼻塞流涕,医生给他开了感冒药,感冒药的作用就是让我们的身体恢复健康,但是服用的过程中,张三感冒的症状的确是减轻了,但同时他感到浑身乏力、嗜睡,这就副作用,即意料之外的结果。 事实上,React 18的文档提到的更多的是purity,即纯度,这其实是函数式编程的理念,这与React 17文档中提到的无副作用是一个意思,react hooks函数式组件实际上就是函数式编程理念的体现。编写纯函数带来了一定的心智负担,但随着开发者对其接受度的提高,新文档中大量使用了purity进行相关描述。文档中提到,纯函数带来了以下优势: 多环境运行。例如可以运行在服务端,因为同样的输入,总是对应同样的输出,因此组件可以被其他人复用; 减少重复渲染。如果函数组件的输入没有改变,直接复用就好啦,不需要重复渲染。 随时中断渲染。在渲染层级较深的组件树时,数据发生了改变,那么React可以马上重新开始渲染,而不用等待过时的渲染完成。因此StrictMode就是在开发中帮助我们进行检测,保证我们编写的函数组件都是 '纯' 的,这也就解释了为什么开头提到的为什么组件会执行两次,StrictMode会多执行一次,两次执行的结果相同,证明我们编写的的确是纯函数。 实例以下列举了一些引发StrictMode的其他例子 在函数内部修改一个已经存在的变量 let guest = 0; function Cup() { // Bad: changing a preexisting variable! guest = guest + 1; return Tea cup for guest #{guest}; } export default function TeaSet() { return ( ) } 复制代码很明显,当这个组件执行多次,guest的值是逐渐增长的,回想一下纯函数的定义,上述函数组件不是纯函数,因此StrictMode会进行警告。 useState下面的组件定义了counter变量,我们给useState传入了初始化函数,组件首次运行时会执行一次这个函数,返回的值作为counter的初始值,在之后的执行中这个初始化函数会被忽略。 此外,在使用setState改变counter的值时,我们为其提供了一个updater函数,当点击按钮时会更新counter的值。 因此,理论上首先会打印一次initializer,然后每按一次按钮,都会打印一次updater import React, { useState } from "react"; export default function Count2() { const [counter, setCounter] = useState(() => { console.log('initializer') return 0 }); const handleClick = function() { setCounter(() => { console.log('updater') return counter+1 }) } return ( {counter} 增加 ); } 复制代码demo 而事实上,StrictMode会帮我们把这两个函数都调用两次,保证其为纯函数。 总结React.StrictMode在开发模式下会重复调用组件,保证我们编写的组件 检测意外的副作用,确保函数组件时纯函数 对于在应用中使用已经废弃、过时的方法会发出警告 仅在开发者模式下运行,不影响生产构建 参考Side Effects: (un)intended consequences # 略微探究React StrictMode两次渲染的问题 严格模式 |
CopyRight 2018-2019 实验室设备网 版权所有 |